package com.szjz.excel.importexport;

import com.szjz.excel.importexport.annotation.ExportMapping;
import com.szjz.excel.importexport.annotation.ImportMapping;
import com.szjz.excel.importexport.exception.ImportExportException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;


@Slf4j
class FieldInfoUtil {

    private static final Logger logger = LoggerFactory.getLogger(FieldInfoUtil.class);

    /**
     * 获取clazz需要匹配的所有字段, 及对应的注解和setter方法
     *
     * @param clazz 模型pojo类
     * @return
     * @throws ImportExportException 没有配置映射关系时抛出
     */
    public static Map<String, ImportFieldInfo> getImportFieldInfo(Class<?> clazz) throws ImportExportException {
        // 获取所有属性
        List<Field> fieldList = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));

        // 移除没有映射的field
        for (Iterator<Field> it = fieldList.iterator(); it.hasNext(); ) {
            Field field = it.next();
            ImportMapping fieldAnnotation = field.getDeclaredAnnotation(ImportMapping.class);
            if (fieldAnnotation == null) {
                it.remove();
            }
        }

        // 验证是否配置了映射关系
        if (fieldList.isEmpty()) {
            throw new ImportExportException("模型" + clazz.getSimpleName() + "没有配置映射关系");
        }

        Map<String, ImportFieldInfo> fieldInfoMap = new HashMap<>();
        for (Field field : fieldList) {
            ImportFieldInfo fieldInfo = new ImportFieldInfo();
            ImportMapping fieldAnnotation = field.getDeclaredAnnotation(ImportMapping.class);
            fieldInfo.setField(field);
            fieldInfo.setFieldAnnotation(fieldAnnotation);
            String fieldName = field.getName();
            // 匹配setter
            try {
                String methodName = "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
                Method setter = clazz.getDeclaredMethod(methodName, field.getType());
                fieldInfo.setSetter(setter);
            } catch (NoSuchMethodException e) {
                logger.warn("模型{}的{}字段缺少setter方法", clazz.getSimpleName(), fieldName);
                throw new ImportExportException("模型" + clazz.getSimpleName() + "的" + fieldName + "字段缺少setter方法");
            } catch (SecurityException e) {
                throw e;
            }

            // 添加到map
            fieldInfoMap.put(fieldName, fieldInfo);
        }

        return fieldInfoMap;
    }

    /**
     * 获取clazz需要匹配的所有字段(根据order排序后), 及对应的注解和getter方法
     *
     * @param clazz 模型pojo类
     * @return
     * @throws ImportExportException 没有配置映射关系时抛出
     */
    public static LinkedHashMap<String, ExportFieldInfo> getExportFieldInfo(Class<?> clazz) throws ImportExportException {
        // 获取所有的字段
        List<Field> fieldList = getAllFields(clazz);

        // 移除没有映射的field
        for (Iterator<Field> it = fieldList.iterator(); it.hasNext(); ) {
            Field field = it.next();
            ExportMapping fieldAnnotation = field.getDeclaredAnnotation(ExportMapping.class);
            if (fieldAnnotation == null) {
                it.remove();
            }
        }

        // 验证是否配置了映射关系
        if (fieldList.isEmpty()) {
            throw new ImportExportException("模型" + clazz.getSimpleName() + "没有配置映射关系");
        }

        // 排序
        fieldList.sort(new Comparator<Field>() {
            @Override
            public int compare(Field o1, Field o2) {
                ExportMapping annotation1 = o1.getDeclaredAnnotation(ExportMapping.class);
                ExportMapping annotation2 = o2.getDeclaredAnnotation(ExportMapping.class);
                return annotation1.column() - annotation2.column();
            }
        });

        LinkedHashMap<String, ExportFieldInfo> fieldInfoMap = new LinkedHashMap<>();
        for (Field field : fieldList) {
            ExportFieldInfo fieldInfo = new ExportFieldInfo();
            ExportMapping fieldAnnotation = field.getDeclaredAnnotation(ExportMapping.class);
            fieldInfo.setField(field);
            fieldInfo.setFieldAnnotation(fieldAnnotation);
            String fn = field.getName();
            // 匹配getter
            String methodName = "get" + Character.toUpperCase(fn.charAt(0)) + fn.substring(1);
            Method getter = null;
            try {
                getter = clazz.getDeclaredMethod(methodName);
            } catch (NoSuchMethodException e) {
                try {
                    getter = clazz.getSuperclass().getDeclaredMethod(methodName);
                } catch (NoSuchMethodException ex) {
                    try {
                        getter = clazz.getSuperclass().getSuperclass().getDeclaredMethod(methodName);
                    } catch (NoSuchMethodException exc) {
                        logger.warn("模型{}的{}字段缺少getter方法", clazz.getSimpleName(), fn);
                        throw new ImportExportException("模型" + clazz.getSimpleName() + "的" + fn + "字段缺少getter方法");
                    }
                    ex.printStackTrace();
                }
            } catch (SecurityException e) {
                throw e;
            }
            fieldInfo.setGetter(getter);
            // 添加到map
            fieldInfoMap.put(fn, fieldInfo);
        }
        return fieldInfoMap;
    }

    /**
     * 获取当前类以及当前类父类（所有的父类）的所有字段
     * 目前只支持2层继承 一般的开发中会有
     * 一个基础的实体类，一个具体的业务实体类，一个继承于该业务实体类的VO类
     *
     * @param clazz
     */
    private static List<Field> getAllFields(Class<?> clazz) {
        // 获取当前类所有属性
        List<Field> fieldList = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        List<Field> superFieldList = null;
        List<Field> superSuperFieldList = null;
        // 获取父类的所有属性
        if (clazz.getSuperclass() != null) {
            superFieldList = Arrays.asList(clazz.getSuperclass().getDeclaredFields());
            fieldList.addAll(superFieldList);
        }
        // 获取父类的父类的所有属性
        if (clazz.getSuperclass().getSuperclass() != null) {
            superSuperFieldList = Arrays.asList(clazz.getSuperclass().getSuperclass().getDeclaredFields());
            fieldList.addAll(superSuperFieldList);
        }
        return fieldList;
    }

}

/**
 * clazz需要匹配的字段, 及对应的注解和setter方法
 */
class ImportFieldInfo {
    private ImportMapping fieldAnnotation;
    private Field field;
    private Method setter;

    public ImportMapping getFieldAnnotation() {
        return fieldAnnotation;
    }

    public void setFieldAnnotation(ImportMapping fieldAnnotation) {
        this.fieldAnnotation = fieldAnnotation;
    }

    public Field getField() {
        return field;
    }

    public void setField(Field field) {
        this.field = field;
    }

    public Method getSetter() {
        return setter;
    }

    public void setSetter(Method setter) {
        this.setter = setter;
    }
}

/**
 * clazz需要匹配的字段, 及对应的注解和getter方法
 */
class ExportFieldInfo {
    private ExportMapping fieldAnnotation;
    private Field field;
    private Method getter;

    public ExportMapping getFieldAnnotation() {
        return fieldAnnotation;
    }

    public void setFieldAnnotation(ExportMapping fieldAnnotation) {
        this.fieldAnnotation = fieldAnnotation;
    }

    public Field getField() {
        return field;
    }

    public void setField(Field field) {
        this.field = field;
    }

    public Method getGetter() {
        return getter;
    }

    public void setGetter(Method getter) {
        this.getter = getter;
    }
}
